﻿#region MIT License
/*
 * Copyright (c) 2010 Michael Gray
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
*/
#endregion

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;

namespace MPCLI
{
    public sealed class Integer : IComparable, IComparable<Integer>, IDisposable
    {
        #region Static Methods

        public static Integer Exponent(Integer pow, uint exp)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Exponent(ref state, ref pow.state, exp);

            return new Integer(state);
        }

        public static Integer Factorial(int value)
        {
            if (value < 0)
            {
                throw new ArgumentOutOfRangeException("value", "The specified value was less than zero.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Factorial(ref state, (uint)value);

            return new Integer(state);
        }

        [CLSCompliant(false)]
        public static Integer Factorial(uint value)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Factorial(ref state, value);

            return new Integer(state);
        }

        /// <summary>
        /// Calculates the greatest common divisor of two instances of <see cref="MPCLI.Integer"/>.
        /// </summary>
        /// <param name="a">The first operator</param>
        /// <param name="b">The second operator</param>
        /// <returns>The GCD in form of <see cref="MPCLI.Integer"/></returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when either of the operators are disposed.</exception>
        public static Integer GCD(Integer a, Integer b)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.GCD(ref state, ref a.state, ref b.state);

            return new Integer(state);
        }

        /// <summary>
        /// Calculates the least common multiple of two instances of <see cref="MPCLI.Integer"/>.
        /// </summary>
        /// <param name="a">The first operator</param>
        /// <param name="b">The second operator</param>
        /// <returns>The LCM in form of <see cref="MPCLI.Integer"/></returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when either of the operators are disposed.</exception>
        public static Integer LCM(Integer a, Integer b)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.LCM(ref state, ref a.state, ref b.state);

            return new Integer(state);
        }

        /// <summary>
        /// Retrieves the next prime in sequence after a given <see cref="MPCLI.Integer"/>.
        /// </summary>
        /// <param name="value">The value at which to begin searching for a prime.</param>
        /// <returns>The next prime number in sequence after the specified value.</returns>
        public static Integer NextPrime(Integer value)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.GetNextPrime(ref state, ref value.state);

            return new Integer(state);
        }

        /// <summary>
        /// Determines the primality of a <see cref="MPCLI.Integer"/>.
        /// </summary>
        /// <param name="value">The value to find the primality of.</param>
        /// <param name="tests">The number of Miller-Rabin test iterations to perform.</param>
        /// <returns>A <see cref="MPCLI.Primality"/> which reflects the results of the test.s</returns>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the number of tests is less than or equal to zero.</exception>
        public static Primality GetPrimality(Integer value, short tests)
        {
            if (tests <= 0)
            {
                throw new ArgumentOutOfRangeException("tests", tests, "The specified number of tests must be greater than zero.");
            }

            return checked((Primality)IntegerCore.IsProbablePrime(ref value.state, tests));
        }

        /// <summary>
        /// Computes the modular exponent with the given values
        /// </summary>
        /// <param name="radix">The radix or base to use</param>
        /// <param name="exponent">The exponent to use</param>
        /// <param name="modulus">The modulus to take of the base raised to the exponent</param>
        /// <returns>The radix taken to the power of the exponent modulo the modulus</returns>
        public static Integer ModularExponent(Integer radix, Integer exponent, Integer modulus)
        {
            if (exponent.size < 0)
            {
                throw new ArgumentOutOfRangeException("exponent", exponent, "The specified exponent must be a positive integer.");
            }

            if (modulus.size == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.ModularExponent(ref state, ref radix.state, ref exponent.state, ref modulus.state);

            return new Integer(state);
        }

        /// <summary>
        /// Computes the modular exponent with the given values
        /// </summary>
        /// <param name="radix">The radix or base to use</param>
        /// <param name="exponent">The exponent to use</param>
        /// <param name="modulus">The modulus to take of the base raised to the exponent</param>
        /// <returns>The radix taken to the power of the exponent modulo the modulus</returns>
        public static Integer ModularExponent(Integer radix, int exponent, Integer modulus)
        {
            if (exponent < 0)
            {
                throw new ArgumentOutOfRangeException("exponent", exponent, "The specified exponent must be a positive integer.");
            }

            if (modulus.size == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.ModularExponent(ref state, ref radix.state, unchecked((uint)exponent), ref modulus.state);

            return new Integer(state);
        }

        /// <summary>
        /// Computes the modular exponent with the given values
        /// </summary>
        /// <param name="radix">The radix or base to use</param>
        /// <param name="exponent">The exponent to use</param>
        /// <param name="modulus">The modulus to take of the base raised to the exponent</param>
        /// <returns>The radix taken to the power of the exponent modulo the modulus</returns>
        [CLSCompliant(false)]
        public static Integer ModularExponent(Integer radix, uint exponent, Integer modulus)
        {
            if (modulus.size == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.ModularExponent(ref state, ref radix.state, exponent, ref modulus.state);

            return new Integer(state);
        }

        /// <summary>
        /// Securely computes the result of the radix raised to the power of the exponent, modulo the modulus.
        /// </summary>
        /// <param name="radix">The radix or base.</param>
        /// <param name="exponent">The exponent.</param>
        /// <param name="modulus">The modulus.</param>
        /// <returns>The result of the radix raised to the power of the exponent, modulo the modulus.</returns>
        /// <exception cref="ArgumentOutOfRangeException">Thrown when the exponent is less than or equal to zero.</exception>
        /// <exception cref="ArgumentException">Thrown when the modulus is even.</exception>
        /// <remarks>
        /// This secure variant of MPCLI.Integer.ModularExponent provides resistance to side-channel attacks.
        /// Additionally, the exponent must be greater than zero and the modulus must be odd.
        /// </remarks>
        public static Integer SecureModularExponent(Integer radix, Integer exponent, Integer modulus)
        {
            if (exponent.size <= 0)
            {
                throw new ArgumentOutOfRangeException("exponent", exponent, "The specified exponent must be greater than zero.");
            }

            if (IntegerCore.IsOdd(ref modulus.state) == 0)
            {
                throw new ArgumentException("The specified modulus must be odd.", "modulus");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.SecureModularExponent(ref state, ref radix.state, ref exponent.state, ref modulus.state);

            return new Integer(state);
        }

        #endregion

        #region Implicit Conversions

        /// <summary>
        /// Converts a <see cref="System.Byte"/> to a <see cref="MPCLI.Integer"/>.
        /// </summary>
        /// <param name="value">The <see cref="System.Byte"/> to convert.</param>
        /// <returns>The value-equivalent <see cref="MPCLI.Integer"/>.</returns>
        public static implicit operator Integer(byte value)
        {
            return new Integer(value);
        }

        /// <summary>
        /// Converts a <see cref="System.Int32"/> to a <see cref="MPCLI.Integer"/>.
        /// </summary>
        /// <param name="value">The <see cref="System.Int32"/> to convert.</param>
        /// <returns>The value-equivalent <see cref="MPCLI.Integer"/>.</returns>
        public static implicit operator Integer(int value)
        {
            return new Integer(value);
        }

        /// <summary>
        /// Converts a <see cref="System.UInt32"/> to a <see cref="MPCLI.Integer"/>.
        /// </summary>
        /// <param name="value">The <see cref="System.UInt32"/> to convert.</param>
        /// <returns>The value-equivalent <see cref="MPCLI.Integer"/>.</returns>
        /// <remarks>This conversion is not compliant with the common language specification.</remarks>
        [CLSCompliant(false)]
        public static implicit operator Integer(uint value)
        {
            return new Integer(value);
        }

        /// <summary>
        /// Converts a <see cref="System.Int64"/> to a <see cref="MPCLI.Integer"/>.
        /// </summary>
        /// <param name="value">The <see cref="System.Int64"/> to convert.</param>
        /// <returns>The value-equivalent <see cref="MPCLI.Integer"/>.</returns>
        public static implicit operator Integer(long value)
        {
            return new Integer(value);
        }

        /// <summary>
        /// Converts a <see cref="System.UInt64"/> to a <see cref="MPCLI.Integer"/>.
        /// </summary>
        /// <param name="value">The <see cref="System.UInt64"/> to convert.</param>
        /// <returns>The value-equivalent <see cref="MPCLI.Integer"/>.</returns>
        /// <remarks>This conversion is not compliant with the common language specification.</remarks>
        [CLSCompliant(false)]
        public static implicit operator Integer(ulong value)
        {
            return new Integer(value);
        }

        #endregion

        #region Explicit Conversions

        /// <summary>
        /// Converts a <see cref="MPCLI.Integer"/> to a <see cref="System.Byte"/>.
        /// </summary>
        /// <param name="value">The <see cref="MPCLI.Integer"/> to convert.</param>
        /// <returns>The result of the potentially lossy conversion.</returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when the <see cref="MPCLI.Integer"/> has been disposed.</exception>
        /// <remarks>This conversion may result in a loss of data.</remarks>
        public static explicit operator byte(Integer value)
        {
            if (value.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (value.state.Size == 0)
            {
                return 0;
            }
            else
            {
                return Marshal.ReadByte(value.state.Data);
            }
        }

        /// <summary>
        /// Converts a <see cref="MPCLI.Integer"/> to a <see cref="System.Byte"/>.
        /// </summary>
        /// <param name="value">The <see cref="MPCLI.Integer"/> to convert.</param>
        /// <returns>The result of the potentially lossy conversion.</returns>
        /// <remarks>
        /// This conversion may result in a loss of data.
        /// This conversion is not compliant with the common language specification.
        /// </remarks>
        [CLSCompliant(false)]
        public static explicit operator sbyte(Integer value)
        {
            if (value.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (value.state.Size == 0)
            {
                return 0;
            }
            else
            {
                return unchecked((sbyte)Marshal.ReadByte(value.state.Data));
            }
        }

        /// <summary>
        /// Converts a <see cref="MPCLI.Integer"/> to a <see cref="System.Int16"/>.
        /// </summary>
        /// <param name="value">The <see cref="MPCLI.Integer"/> to convert.</param>
        /// <returns>The result of the potentially lossy conversion.</returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when the <see cref="MPCLI.Integer"/> has been disposed.</exception>
        /// <remarks>This conversion may result in a loss of data.</remarks>
        public static explicit operator short(Integer value)
        {
            if (value.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (value.state.Size == 0)
            {
                return 0;
            }
            else
            {
                return Marshal.ReadInt16(value.state.Data);
            }
        }

        /// <summary>
        /// Converts a <see cref="MPCLI.Integer"/> to a <see cref="System.UInt16"/>.
        /// </summary>
        /// <param name="value">The <see cref="MPCLI.Integer"/> to convert.</param>
        /// <returns>The result of the potentially lossy conversion.</returns>
        /// <remarks>
        /// This conversion may result in a loss of data.
        /// This conversion is not compliant with the common language specification.
        /// </remarks>
        [CLSCompliant(false)]
        public static explicit operator ushort(Integer value)
        {
            if (value.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (value.state.Size == 0)
            {
                return 0;
            }
            else
            {
                return unchecked((ushort)Marshal.ReadInt16(value.state.Data));
            }
        }

        /// <summary>
        /// Converts a <see cref="MPCLI.Integer"/> to a <see cref="System.Int32"/>.
        /// </summary>
        /// <param name="value">The <see cref="MPCLI.Integer"/> to convert.</param>
        /// <returns>The result of the potentially lossy conversion.</returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when the <see cref="MPCLI.Integer"/> has been disposed.</exception>
        /// <remarks>This conversion may result in a loss of data.</remarks>
        public static explicit operator int(Integer value)
        {
            if (value.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (value.state.Size == 0)
            {
                return 0;
            }
            else
            {
                return Marshal.ReadInt32(value.state.Data);
            }
        }

        /// <summary>
        /// Converts a <see cref="MPCLI.Integer"/> to a <see cref="System.UInt32"/>.
        /// </summary>
        /// <param name="value">The <see cref="MPCLI.Integer"/> to convert.</param>
        /// <returns>The result of the potentially lossy conversion.</returns>
        /// <remarks>
        /// This conversion may result in a loss of data.
        /// This conversion is not compliant with the common language specification.
        /// </remarks>
        [CLSCompliant(false)]
        public static explicit operator uint(Integer value)
        {
            if (value.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (value.state.Size == 0)
            {
                return 0;
            }
            else
            {
                return unchecked((uint)Marshal.ReadInt32(value.state.Data));
            }
        }

        /// <summary>
        /// Converts a <see cref="MPCLI.Integer"/> to a <see cref="System.Int64"/>.
        /// </summary>
        /// <param name="value">The <see cref="MPCLI.Integer"/> to convert.</param>
        /// <returns>The result of the potentially lossy conversion.</returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when the <see cref="MPCLI.Integer"/> has been disposed.</exception>
        /// <remarks>This conversion may result in a loss of data.</remarks>
        public static explicit operator long(Integer value)
        {
            if (value.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (value.state.Allocated >= 2)
            {
                return Marshal.ReadInt64(value.state.Data);
            }
            else if (value.state.Allocated == 1)
            {
                return (long)Marshal.ReadInt32(value.state.Data);
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// Converts a <see cref="MPCLI.Integer"/> to a <see cref="System.UInt64"/>.
        /// </summary>
        /// <param name="value">The <see cref="MPCLI.Integer"/> to convert.</param>
        /// <returns>The result of the potentially lossy conversion.</returns>
        /// <remarks>
        /// This conversion may result in a loss of data.
        /// This conversion is not compliant with the common language specification.
        /// </remarks>
        [CLSCompliant(false)]
        public static explicit operator ulong(Integer value)
        {
            if (value.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (value.state.Allocated >= 2)
            {
                return unchecked((ulong)Marshal.ReadInt64(value.state.Data));
            }
            else if (value.state.Allocated == 1)
            {
                return unchecked((ulong)Marshal.ReadInt32(value.state.Data));
            }
            else
            {
                return 0;
            }
        }

        #endregion

        #region Arithmetic Operators (Integer, Integer)

        public static Integer operator ++(Integer operand)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Add(ref state, ref operand.state, 1);

            return new Integer(state);
        }

        public static Integer operator --(Integer operand)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Subtract(ref state, ref operand.state, 1);

            return new Integer(state);
        }

        public static Integer operator +(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Add(ref state, ref first.state, ref second.state);

            return new Integer(state);
        }

        public static Integer operator -(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Subtract(ref state, ref first.state, ref second.state);

            return new Integer(state);
        }

        public static Integer operator *(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Multiply(ref state, ref first.state, ref second.state);

            return new Integer(state);
        }

        public static Integer operator /(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            if (second.state.Size == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero.");
            }
            
            if (first.state.Size == 0 || second.state.Size > first.state.Size)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Divide(ref state, ref first.state, ref second.state);

            return new Integer(state);
        }

        public static Integer operator %(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            if (second.state.Size == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero");
            }

            if (first.state.Size == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Modulus(ref state, ref first.state, ref second.state);

            return new Integer(state);
        }

        #endregion

        #region Arithmetic Operators (Integer, int)

        public static Integer operator +(Integer first, int second)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, ref first.state);

            if (second == 0)
            {
                return new Integer(state);
            }
            else if (second > 0)
            {
                IntegerCore.Add(ref state, ref first.state, (uint)second);
            }
            else
            {
                IntegerCore.Subtract(ref state, ref first.state, (uint)-second);
            }

            return new Integer(state);
        }

        public static Integer operator -(Integer first, int second)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, ref first.state);

            if (second == 0)
            {
                return new Integer(state);
            }
            else if (second > 0)
            {
                IntegerCore.Subtract(ref state, ref first.state, (uint)second);
            }
            else
            {
                IntegerCore.Add(ref state, ref first.state, (uint)-second);
            }

            return new Integer(state);
        }

        public static Integer operator *(Integer first, int second)
        {
            if (first.state.Size == 0 || second == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Multiply(ref state, ref first.state, second);

            return new Integer(state);
        }

        public static Integer operator /(Integer first, int second)
        {
            if (second == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero.");
            }
            else if (first.state.Size == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);

            if (second > 0)
            {
                IntegerCore.Divide(ref state, ref first.state, (uint)second);
            }
            else
            {
                IntegerCore.Divide(ref state, ref first.state, (uint)-second);
                state.Size = -state.Size;
            }

            return new Integer(state);
        }

        #endregion

        #region Arithmetic Operators (int, Integer)

        public static Integer operator +(int first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, ref second.state);

            if (first == 0)
            {
                return new Integer(state);
            }
            
            if (first > 0)
            {
                IntegerCore.Add(ref state, ref state, (uint)first);
            }
            else
            {
                IntegerCore.Subtract(ref state, ref state, (uint)-first);
            }

            return new Integer(state);
        }

        public static Integer operator -(int first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, ref second.state);

            if (first == 0)
            {
                return new Integer(state);
            }

            if (first > 0)
            {
                IntegerCore.Subtract(ref state, ref state, (uint)first);
            }
            else
            {
                IntegerCore.Add(ref state, ref state, (uint)-first);
            }

            return new Integer(state);
        }

        public static Integer operator *(int first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            if (first == 0 || second.state.Size == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Multiply(ref state, ref second.state, first);

            return new Integer(state);
        }

        public static Integer operator /(int first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            if (second.state.Size == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero");
            }
            
            if (second.state.Size > 1)
            {
                return first;
            }

            if (first == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);

            IntegerState numerator = new IntegerState();
            IntegerCore.Initialize(ref numerator, first);

            IntegerCore.Divide(ref state, ref numerator, ref second.state);
            IntegerCore.Free(ref numerator);

            return new Integer(state);
        }

        public static Integer operator %(int first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            if (second.state.Size == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero");
            }

            if (first == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, first);
            IntegerCore.Modulus(ref state, ref state, ref second.state);

            return new Integer(state);
        }

        #endregion

        #region Arithmetic Operators (Integer, uint)

        [CLSCompliant(false)]
        public static Integer operator +(Integer first, uint second)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, ref first.state);

            if (second == 0)
            {
                return new Integer(state);
            }

            IntegerCore.Add(ref state, ref first.state, second);

            return new Integer(state);
        }

        [CLSCompliant(false)]
        public static Integer operator -(Integer first, uint second)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, ref first.state);

            if (second == 0)
            {
                return new Integer(state);
            }

            IntegerCore.Subtract(ref state, ref first.state, (uint)second);

            return new Integer(state);
        }

        [CLSCompliant(false)]
        public static Integer operator *(Integer first, uint second)
        {
            if (first.state.Size == 0 || second == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Multiply(ref state, ref first.state, second);

            return new Integer(state);
        }

        [CLSCompliant(false)]
        public static Integer operator /(Integer first, uint second)
        {
            if (second == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero.");
            }
            
            if (first.state.Size == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Divide(ref state, ref first.state, second);

            return new Integer(state);
        }

        [CLSCompliant(false)]
        public static Integer operator %(Integer first, uint second)
        {
            if (second == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero.");
            }

            if (first.state.Size == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Modulus(ref first.state, second);

            return new Integer(state);
        }

        #endregion

        #region Arithmetic Operators (uint, Integer)

        [CLSCompliant(false)]
        public static Integer operator +(uint first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, ref second.state);

            if (first == 0)
            {
                return new Integer(state);
            }
            
            if (first > 0)
            {
                IntegerCore.Add(ref state, ref state, (uint)first);
            }
            else
            {
                IntegerCore.Subtract(ref state, ref state, (uint)-first);
            }

            return new Integer(state);
        }

        [CLSCompliant(false)]
        public static Integer operator -(uint first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, ref second.state);

            if (first == 0)
            {
                return new Integer(state);
            }
            
            if (first > 0)
            {
                IntegerCore.Subtract(ref state, ref state, (uint)first);
            }
            else
            {
                IntegerCore.Add(ref state, ref state, (uint)-first);
            }

            return new Integer(state);
        }

        [CLSCompliant(false)]
        public static Integer operator *(uint first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            if (first == 0 || second.state.Size == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Multiply(ref state, ref second.state, first);

            return new Integer(state);
        }

        [CLSCompliant(false)]
        public static Integer operator /(uint first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            if (second.state.Size == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero");
            }
            
            if (second.state.Size > 1)
            {
                return first;
            }

            if (first == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);

            IntegerState numerator = new IntegerState();
            IntegerCore.Initialize(ref numerator, first);

            IntegerCore.Divide(ref state, ref numerator, ref second.state);
            IntegerCore.Free(ref numerator);

            return new Integer(state);
        }

        [CLSCompliant(false)]
        public static Integer operator %(uint first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            if (second.state.Size == 0)
            {
                throw new DivideByZeroException("Attempted to divide by zero");
            }

            if (first == 0)
            {
                return Integer.Zero;
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state, first);
            IntegerCore.Modulus(ref state, ref state, ref second.state);

            return new Integer(state);
        }

        #endregion

        #region Arithmetic Operators (Integer, long)

        public static Integer operator +(Integer first, long second)
        {
            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first + converted;
            }

            return result;
        }

        public static Integer operator -(Integer first, long second)
        {
            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first - converted;
            }

            return result ;
        }

        public static Integer operator *(Integer first, long second)
        {
            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first * converted;
            }

            return result;
        }

        public static Integer operator /(Integer first, long second)
        {
            if (second == 0)
            {
                throw new DivideByZeroException();
            }

            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first / converted;
            }

            return result;
        }

        public static Integer operator %(Integer first, long second)
        {
            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first % converted;
            }

            return result;
        }

        #endregion

        #region Arithmetic Operators (long, Integer)

        public static Integer operator +(long first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted + second;
            }

            return result;
        }

        public static Integer operator -(long first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted - second;
            }

            return result;
        }

        public static Integer operator *(long first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted * second;
            }

            return result;
        }

        public static Integer operator /(long first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted / second;
            }

            return result;
        }

        public static Integer operator %(long first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted % second;
            }

            return result;
        }

        #endregion

        #region Arithmetic Operators (Integer, ulong)

        [CLSCompliant(false)]
        public static Integer operator +(Integer first, ulong second)
        {
            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first + converted;
            }

            return result;
        }

        [CLSCompliant(false)]
        public static Integer operator -(Integer first, ulong second)
        {
            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first - converted;
            }

            return result;
        }

        [CLSCompliant(false)]
        public static Integer operator *(Integer first, ulong second)
        {
            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first * converted;
            }

            return result;
        }

        [CLSCompliant(false)]
        public static Integer operator /(Integer first, ulong second)
        {
            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first / converted;
            }

            return result;
        }

        [CLSCompliant(false)]
        public static Integer operator %(Integer first, ulong second)
        {
            Integer result;
            using (Integer converted = new Integer(second))
            {
                result = first % converted;
            }

            return result;
        }

        #endregion

        #region Arithmetic Operators (ulong, Integer)

        [CLSCompliant(false)]
        public static Integer operator +(ulong first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted + second;
            }

            return result;
        }

        [CLSCompliant(false)]
        public static Integer operator -(ulong first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted - second;
            }

            return result;
        }

        [CLSCompliant(false)]
        public static Integer operator *(ulong first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted * second;
            }

            return result;
        }

        [CLSCompliant(false)]
        public static Integer operator /(ulong first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted / second;
            }

            return result;
        }

        [CLSCompliant(false)]
        public static Integer operator %(ulong first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            Integer result;
            using (Integer converted = new Integer(first))
            {
                result = converted % second;
            }

            return result;
        }

        #endregion

        #region Bitwise Arithmetic Operators (Integer, Integer)

        public static Integer operator -(Integer operand)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.Negate(ref state, ref operand.state);

            return new Integer(state);
        }

        public static Integer operator ~(Integer operand)
        {
            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref operand.state);
            IntegerCore.Complement(ref state, ref state);

            return new Integer(state);
        }

        public static Integer operator &(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.And(ref state, ref first.state, ref second.state);

            return new Integer(state);
        }

        public static Integer operator |(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.InclusiveOr(ref state, ref first.state, ref second.state);

            return new Integer(state);
        }

        public static Integer operator ^(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                throw new NullReferenceException("The operand is null.");
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The operand is disposed.");
            }

            IntegerState state = new IntegerState();
            IntegerCore.Initialize(ref state);
            IntegerCore.ExclusiveOr(ref state, ref first.state, ref second.state);

            return new Integer(state);
        }

        public static Integer operator >>(Integer operand, int count)
        {
            IntegerState state = new IntegerState();

            if (count > 0)
            {
                IntegerCore.Initialize(ref state);
                IntegerCore.ShiftRight(ref state, ref operand.state, (uint)count);
            }
            else if (count < 0)
            {
                IntegerCore.Initialize(ref state);
                IntegerCore.ShiftLeft(ref state, ref operand.state, (uint)-count);
            }
            else
            {
                IntegerCore.Initialize(ref operand.state);
            }

            return new Integer(state);
        }

        public static Integer operator <<(Integer operand, int count)
        {
            IntegerState state = new IntegerState();

            if (count > 0)
            {
                IntegerCore.Initialize(ref state);
                IntegerCore.ShiftLeft(ref state, ref operand.state, (uint)count);
            }
            else if (count < 0)
            {
                IntegerCore.Initialize(ref state);
                IntegerCore.ShiftRight(ref state, ref operand.state, (uint)-count);
            }
            else
            {
                IntegerCore.Initialize(ref operand.state);
            }

            return new Integer(state);
        }

        #endregion

        #region Comparison Operators (Integer, Integer)

        public static bool operator >(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                return true;
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The comparand is disposed.");
            }

            if (first.state.Data == second.state.Data)
            {
                return false;
            }

            return (IntegerCore.Compare(ref first.state, ref second.state) > 0);
        }

        public static bool operator >=(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null) || first.state.Data == second.state.Data)
            {
                return true;
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The comparand is disposed.");
            }

            return (IntegerCore.Compare(ref first.state, ref second.state) > 0);
        }

        public static bool operator <(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                return false;
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The comparand is disposed.");
            }

            if (first.state.Data == second.state.Data)
            {
                return false;
            }

            return (IntegerCore.Compare(ref first.state, ref second.state) < 0);
        }

        public static bool operator <=(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                return false;
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The comparand is disposed.");
            }

            if (first.state.Data == second.state.Data)
            {
                return true;
            }

            return (IntegerCore.Compare(ref first.state, ref second.state) < 0);
        }

        public static bool operator ==(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                return false;
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The comparand is disposed.");
            }

            if (first.state.Data == second.state.Data)
            {
                return true;
            }

            return (IntegerCore.Compare(ref first.state, ref second.state) == 0);
        }

        public static bool operator !=(Integer first, Integer second)
        {
            if (Object.ReferenceEquals(second, null))
            {
                return true;
            }

            if (second.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The comparand is disposed.");
            }

            if (first.state.Data == second.state.Data)
            {
                return false;
            }

            return (IntegerCore.Compare(ref first.state, ref second.state) != 0);
        }

        #endregion

        #region Fields

        private IntegerState state;
        private bool disposed;

        private readonly int size;
        private readonly bool sign;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the size of the <see cref="MPCLI.Integer"/> in bits
        /// </summary>
        /// <remarks>
        /// This is the size of the <see cref="MPCLI.Integer"/> data in memory, rounded towards infinity to the nearest multiple of 32
        /// Zero always has a Size of 0
        /// </remarks>
        /// <exception cref="ObjectDisposedException">Thrown when the <see cref="MPCLI.Integer"/> has been disposed</exception>
        public int Size
        {
            get
            {
                if (this.disposed)
                {
                    throw new ObjectDisposedException("MPCLI.Integer");
                }

                return this.size;
            }
        }

        /// <summary>
        /// Gets a value indicating if the <see cref="MPCLI.Integer"/> is positive
        /// </summary>
        /// <remarks>
        /// Zero always has a Sign of true
        /// </remarks>
        /// <exception cref="ObjectDisposedException">Thrown when the <see cref="MPCLI.Integer"/> has been disposed</exception>
        public bool Sign
        {
            get
            {
                if (this.disposed)
                {
                    throw new ObjectDisposedException("MPCLI.Integer");
                }

                return this.sign;
            }
        }

        #endregion

        #region Constructors

        private Integer(IntegerState state)
        {
            this.state = state;

            if (state.Size >= 0)
            {
                this.size = state.Size * 32;
                this.sign = true;
            }
            else
            {
                this.size = state.Size * -32;
                this.sign = false;
            }

            GC.AddMemoryPressure(IntegerCore.StateSize + state.Allocated);
        }

        /// <summary>
        /// Intializes a new instance of <see cref="MPCLI.Integer"/> with the specified value
        /// </summary>
        /// <param name="value">A little endian ordered array of 8-bit unsigned bytes</param>
        public Integer(byte[] value, bool negate)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            
            if (value.Length == 0)
            {
                throw new ArgumentException("The specified value must contain at least one element");
            }

            int index;
            for (index = value.Length - 1; index != 0; index--)
            {
                if (value[index] != 0)
                {
                    break;
                }
            }

            if (index == 0 && value[0] == 0)
            {
                this.state.Data = IntPtr.Zero;
                this.state.Allocated = 0;
                this.state.Size = 0;
                GC.AddMemoryPressure(IntegerCore.StateSize);

                this.size = 0;
                this.sign = true;

                this.disposed = false;
                return;
            }
            else if (index != value.Length - 1)
            {
                Array.Resize<byte>(ref value, index + 1);
            }

            this.state = new IntegerState();
            if (value.Length <= sizeof(int))
            {
                IntegerCore.Allocate(ref this.state, 32);

                switch (value.Length)
                {
                    case 1:
                        Marshal.WriteInt32(this.state.Data, unchecked(value[0]));
                        break;

                    case 2:
                        Marshal.WriteInt32(this.state.Data, unchecked((value[1] << 8) | value[0]));
                        break;

                    case 3:
                        Marshal.WriteInt32(this.state.Data, unchecked((value[2] << 16) | (value[1] << 8) | value[0]));
                        break;

                    case 4:
                        Marshal.WriteInt32(this.state.Data, unchecked((value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]));
                        break;
                }

                this.state.Size = 1;

                GC.AddMemoryPressure(IntegerCore.StateSize + sizeof(int));
                this.size = 32;
            }
            else
            {
                int[] data;
                if ((value.Length & (value.Length - 1)) == 0)
                {
                    data = new int[value.Length >> 2];
                    for (int i = 0, k = 0; i != data.Length; i++, k += sizeof(int))
                    {
                        data[i] = unchecked((value[k + 3] << 24) | (value[k + 2] << 16) | (value[k + 1] << 8) | value[k]);
                    }
                }
                else
                {
                    index = 0;
                    data = new int[(value.Length >> 2) + 1];
                    for (int i = 0; i != data.Length - 1; i++, index += sizeof(int))
                    {
                        data[i] = unchecked((value[index + 3] << 24) | (value[index + 2] << 16) | (value[index + 1] << 8) | value[index]);
                    }

                    switch (value.Length - index)
                    {
                        case 1:
                            data[data.Length - 1] = value[value.Length - 1];
                            break;

                        case 2:
                            data[data.Length - 1] = unchecked((value[value.Length - 1] << 8) | (value[value.Length - 2]));
                            break;

                        case 3:
                            data[data.Length - 1] = unchecked((value[value.Length - 1] << 16 | (value[value.Length - 2] << 8) | value[value.Length - 3]));
                            break;
                    }
                }

                System.Diagnostics.Debug.Assert(data[data.Length - 1] != 0, "A trailing zero was present in the result");

                IntegerCore.Allocate(ref this.state, checked((uint)data.Length * 32));
                Marshal.Copy(data, 0, this.state.Data, data.Length);
                this.state.Size = data.Length;

                GC.AddMemoryPressure(checked(IntegerCore.StateSize + this.state.Allocated));
                this.size = checked(data.Length * 32);
            }

            if (negate)
            {
                this.state.Size = -this.state.Size;
                this.sign = false;
            }
            else
            {
                this.sign = true;
            }

            this.disposed = false;
        }

        /// <summary>
        /// Intializes a new instance of <see cref="MPCLI.Integer"/> with the specified value
        /// </summary>
        /// <param name="value">A little endian ordered array of 32-bit signed integers</param>
        public Integer(int[] value, bool negate)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            
            if (value.Length == 0)
            {
                throw new ArgumentException("The specified value must contain at least one element");
            }
            
            if (value.Length == 1 && value[0] == 0)
            {
                this.state.Data = IntPtr.Zero;
                this.state.Allocated = 0;
                this.state.Size = 0;
                GC.AddMemoryPressure(IntegerCore.StateSize);

                this.size = 0;
                this.sign = true;

                return;
            }

            int index;
            for (index = value.Length - 1; index != 0; index--)
            {
                if (value[index] != 0)
                {
                    break;
                }
            }

            if (index != value.Length - 1)
            {
                // TODO: See what optimizations can be done with this here
                Array.Resize<int>(ref value, index);
            }

            this.state = new IntegerState();

            IntegerCore.Allocate(ref this.state, (uint)this.size * 32);
            Marshal.Copy(value, 0, this.state.Data, value.Length);
            this.state.Allocated = value.Length;
            this.state.Size = value.Length;

            GC.AddMemoryPressure(checked(IntegerCore.StateSize + this.state.Allocated));

            if (negate)
            {
                this.state.Size = -this.state.Size;
                this.sign = false;
            }
            else
            {
                this.sign = true;
            }

            this.disposed = false;
            this.size = checked(value.Length * 32);
        }

        /// <summary>
        /// Intializes a new instance of <see cref="MPCLI.Integer"/> with the specified value
        /// </summary>
        /// <param name="value">A little endian ordered array of 32-bit unsigned integers</param>
        [CLSCompliant(false)]
        public Integer(uint[] value, bool negate)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            if (value.Length == 0)
            {
                throw new ArgumentException("The specified value must contain at least one element");
            }
            
            if (value.Length == 1 && value[0] == 0)
            {
                this.state.Data = IntPtr.Zero;
                this.state.Allocated = 0;
                this.state.Size = 0;
                GC.AddMemoryPressure(IntegerCore.StateSize);

                this.size = 0;
                this.sign = true;

                return;
            }

            int index;
            for (index = value.Length - 1; index != 0; index--)
            {
                if (value[index] != 0)
                {
                    break;
                }
            }

            int[] data = new int[index];
            for (int i = 0; i != data.Length; i++)
            {
                unchecked
                {
                    data[i] = (int)value[i];
                }
            }

            this.state = new IntegerState();
            IntegerCore.Allocate(ref this.state, (uint)this.size * 32);

            Marshal.Copy(data, 0, this.state.Data, data.Length);
            this.state.Allocated = data.Length;
            this.state.Size = data.Length;

            GC.AddMemoryPressure(checked(IntegerCore.StateSize + this.state.Allocated));

            if (negate)
            {
                this.state.Size = -this.state.Size;
                this.sign = false;
            }
            else
            {
                this.sign = true;
            }

            this.disposed = false;
            this.size = checked(data.Length * 32);
        }

        /// <summary>
        /// Intializes a new instance of <see cref="MPCLI.Integer"/> with the specified value
        /// </summary>
        /// <param name="value">A 32-bit signed integer</param>
        public Integer(int value)
        {
            this.state = new IntegerState();

            if (value == 0)
            {
                this.state.Data = IntPtr.Zero;
                this.state.Allocated = 0;
                this.state.Size = 0;
                GC.AddMemoryPressure(IntegerCore.StateSize);

                this.size = 0;
                this.sign = true;
            }
            else
            {
                IntegerCore.Initialize(ref this.state, value);
                GC.AddMemoryPressure(IntegerCore.StateSize + sizeof(int));

                if (value > 0)
                {
                    this.sign = true;
                }
                else
                {
                    this.sign = false;
                }
            }

            this.disposed = false;
            this.size = 32;
        }

        /// <summary>
        /// Intializes a new instance of <see cref="MPCLI.Integer"/> with the specified value
        /// </summary>
        /// <param name="value">A 32-bit unsigned integer</param>
        [CLSCompliant(false)]
        public Integer(uint value)
        {
            this.state = new IntegerState();
            IntegerCore.Initialize(ref this.state, unchecked((int)value));
            GC.AddMemoryPressure(IntegerCore.StateSize + sizeof(uint));

            this.disposed = false;
            this.size = 32;
            this.sign = true;
        }

        /// <summary>
        /// Intializes a new instance of <see cref="MPCLI.Integer"/> with the specified value
        /// </summary>
        /// <param name="value">A 64-bit signed integer</param>
        public Integer(long value)
        {
            this.state = new IntegerState();

            if (value == 0)
            {
                this.state.Data = IntPtr.Zero;
                this.state.Allocated = 0;
                this.state.Size = 0;
                GC.AddMemoryPressure(IntegerCore.StateSize);

                this.size = 0;
                this.sign = true;
            }
            else if (value <= UInt32.MaxValue)
            {
                if (value > 0)
                {
                    IntegerCore.Initialize(ref this.state, checked((uint)value));
                }
                else
                {
                    IntegerCore.Initialize(ref this.state, checked((int)value));
                }

                GC.AddMemoryPressure(IntegerCore.StateSize + sizeof(int));
                this.size = 32;
            }
            else
            {
                IntegerCore.Allocate(ref this.state, sizeof(long) * 32);
                this.state.Allocated = 2;
                GC.AddMemoryPressure(IntegerCore.StateSize + sizeof(long));

                if (value > 0)
                {
                    Marshal.WriteInt64(this.state.Data, value);

                    this.state.Size = 2;
                    this.sign = true;
                }
                else
                {
                    Marshal.WriteInt64(this.state.Data, -value);

                    this.state.Size = -2;
                    this.sign = false;
                }

                this.size = 64;
            }

            this.disposed = false;
        }

        /// <summary>
        /// Intializes a new instance of <see cref="MPCLI.Integer"/> with the specified value
        /// </summary>
        /// <param name="value">A 64-bit unsigned integer</param>
        [CLSCompliant(false)]
        public Integer(ulong value)
        {
            this.state = new IntegerState();

            if (value == 0)
            {
                this.state.Data = IntPtr.Zero;
                this.state.Allocated = 0;
                this.state.Size = 0;

                this.size = 0;
            }
            else if ((value & 0xFFFFFFFF00000000) == 0)
            {
                IntegerCore.Initialize(ref this.state, checked((uint)value));
                GC.AddMemoryPressure(IntegerCore.StateSize + sizeof(uint));

                this.size = 32;
            }
            else
            {
                IntegerCore.Allocate(ref this.state, sizeof(long) * 32);
                this.state.Allocated = 2;
                this.state.Size = 2;
                GC.AddMemoryPressure(IntegerCore.StateSize + sizeof(ulong));

                Marshal.WriteInt64(this.state.Data, unchecked((long)value));

                this.size = 64;
            }

            this.sign = true;
            this.disposed = false;
        }

        /// <summary>
        /// Intializes a new instance of <see cref="MPCLI.Integer"/> with the specified value
        /// </summary>
        /// <param name="value">A string representation of an <see cref="MPCLI.Integer"/> with a radix of 10</param>
        public Integer(string value)
        {
            this.state = new IntegerState();
            int error = IntegerCore.Initialize(ref this.state, value, 10);

            if (error == 0)
            {
                if (this.state.Size < 0)
                {
                    this.size = this.state.Size * -32;
                }
                else
                {
                    this.size = this.state.Size * 32;
                }

                GC.AddMemoryPressure(checked(IntegerCore.StateSize + this.state.Allocated));

                this.disposed = false;
            }
            else
            {
                IntegerCore.Free(ref this.state);
                this.disposed = true;

                throw new FormatException("The specified string must be an integer represented in radix 10");
            }
        }

        /// <summary>
        /// Intializes a new instance of <see cref="MPCLI.Integer"/> with the specified value
        /// </summary>
        /// <param name="value">A string representation of an <see cref="MPCLI.Integer"/></param>
        /// <param name="radix">The radix which the string representation is in</param>
        public Integer(string value, int radix)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            
            if (value.Length == 0)
            {
                throw new ArgumentException("The specified value must contain at least one character", "value");
            }
            
            if (radix < 2 || radix > 62)
            {
                throw new ArgumentOutOfRangeException("radix", radix, "The specified radix must be between 2 and 62 inclusive");
            }

            this.state = new IntegerState();
            int error = IntegerCore.Initialize(ref this.state, value, radix);

            if (error == 0)
            {
                if (this.state.Size < 0)
                {
                    this.size = this.state.Size * -32;
                }
                else
                {
                    this.size = this.state.Size * 32;
                }

                GC.AddMemoryPressure(checked(IntegerCore.StateSize + this.state.Allocated));

                this.disposed = false;
            }
            else
            {
                IntegerCore.Free(ref this.state);
                this.disposed = true;

                throw new FormatException("The specified string must be an integer represented in the specified radix");
            }
        }

        #endregion

        #region Destructor

        /// <summary>
        /// Disposes of the <see cref="MPCLI.Integer"/> and releases all associated managed and unmanaged resources.
        /// </summary>
        ~Integer()
        {
            if (!this.disposed)
            {
                this.Dispose();
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Gets the hash code of the object
        /// </summary>
        /// <returns>The 32-bit truncated memory address of the start of the numerical data</returns>
        /// <exception cref="ObjectDisposedException">Thrown when the current <see cref="MPCLI.Integer"/> has been disposed</exception>
        public override int GetHashCode()
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            return this.state.Data.GetHashCode();
        }

        /// <summary>
        /// Determines if an object has value-based equality to the <see cref="MPCLI.Integer"/>
        /// </summary>
        /// <param name="obj">The object to check equality with</param>
        /// <returns>True of equal by value, otherwise false</returns>
        /// <exception cref="ObjectDisposedException">Thrown when the comparing or comparand <see cref="MPCLI.Integer"/> is disposed</exception>
        public override bool Equals(object obj)
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            Integer other = obj as Integer;
            if (Object.ReferenceEquals(other, null))
            {
                return false;
            }

            if (other.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The comparand is disposed.");
            }

            return (IntegerCore.Compare(ref this.state, ref other.state) == 0);
        }

        /// <summary>
        /// Converts the <see cref="MPCLI.Integer"/> to a radix-10 string representation.
        /// </summary>
        /// <returns>A radix 10 string representation of the <see cref="MPCLI.Integer"/>.</returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when the current <see cref="MPCLI.Integer"/> has been disposed.</exception>
        public override string ToString()
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (this.state.Size == 0)
            {
                return "0";
            }

            int size = IntegerCore.GetSize(ref this.state, 10);

            IntPtr buffer;
            if (this.state.Size > 0)
            {
                buffer = Marshal.AllocHGlobal(checked(size + 1));
            }
            else
            {
                buffer = Marshal.AllocHGlobal(checked(size + 2));
            }

            return IntegerCore.GetString(buffer, 10, ref this.state);
        }

        /// <summary>
        /// Covnerts the <see cref="MPCLI.Integer"/> to a string representation in the specified radix.
        /// </summary>
        /// <param name="radix">The radix which the string representation will be in</param>
        /// <returns>A string representing the current <see cref="MPCLI.Integer"/> in the specified radix.</returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when the current <see cref="MPCLI.Integer"/> has been disposed.</exception>
        public string ToString(int radix)
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }
            
            if (radix < 2 || radix > 62)
            {
                throw new ArgumentOutOfRangeException("radix", radix, "The specified radix must be between 2 and 62 inclusive");
            }

            // Force GNU MP to make these upper-case
            if (radix <= 36)
            {
                radix = -radix;
            }

            if (this.state.Size == 0)
            {
                return "0";
            }

            int size = IntegerCore.GetSize(ref this.state, radix);

            IntPtr buffer;
            if (this.state.Size > 0)
            {
                buffer = Marshal.AllocHGlobal(checked(size + 1));
            }
            else
            {
                buffer = Marshal.AllocHGlobal(checked(size + 2));
            }

            return IntegerCore.GetString(buffer, -radix, ref this.state);
        }

        /// <summary>
        /// Converts the current <see cref="MPCLI.Integer"/> to an array of <see cref="System.Byte"/>.
        /// </summary>
        /// <returns>An array of <see cref="System.Byte"/> representing the <see cref="MPCLI.Integer"/></returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when the current <see cref="MPCLI.Integer"/> has been disposed.</exception>
        public byte[] ToArray()
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            int length = checked((this.state.Size >= 0 ? this.state.Size : -this.state.Size) * sizeof(uint));

            byte[] result = new byte[length];
            Marshal.Copy(this.state.Data, result, 0, length);

            return result;
        }

        #region IComparable Members

        /// <summary>
        /// Compares the <see cref="MPCLI.Integer"/> to a <see cref="System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare to</param>
        /// <returns>1 if the <see cref="MPCLI.Integer"/> is greater than, 0 if it is equal, or -1 if it is less than the specified <see cref="System.Object"/></returns>
        /// <exception cref="System.ObjectDisposedException">Thrown when the comparing or comparand <see cref="MPCLI.Integer"/> is disposed.</exception>
        /// <exception cref="System.ArgumentNullException">Thrown when the <see cref="System.Object"/> is null.</exception>
        /// <exception cref="System.ArgumentException">Thrown when the specified <see cref="System.Object"/> is not of type <see cref="MPCLI.Integer"/>.</exception>
        public int CompareTo(object obj)
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (obj == null)
            {
                throw new ArgumentNullException("obj");
            }

            Integer other = obj as Integer;

            if (Object.ReferenceEquals(other, null))
            {
                throw new ArgumentException("The specified object must be of type MPCLI.Integer");
            }

            if (other.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The comparand is disposed.");
            }

            return IntegerCore.Compare(ref this.state, ref other.state);
        }

        #endregion

        #region IComparable<Integer> Members

        /// <summary>
        /// Compares two <see cref="MPCLI.Integer"/> to eachother.
        /// </summary>
        /// <param name="other">The <see cref="MPCLI.Integer"/> to compare to.</param>
        /// <returns>1 if the comparing instance is greater than, 0 if it is equal to, or -1 if it is less than the specified <see cref="MPCLI.Integer"/>.</returns>
        /// <exception cref="System.ObjectDisposed">Thrown when the comparing or comparand <see cref="MPCLI.Integer"/> is disposed.</exception>
        /// <exception cref="System.ArgumentNullException">Thrown when the compared <see cref="MPCLI.Integer"/> is null.</exception>
        public int CompareTo(Integer other)
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer");
            }

            if (Object.ReferenceEquals(other, null))
            {
                throw new ArgumentNullException("other");
            }

            if (other.disposed)
            {
                throw new ObjectDisposedException("MPCLI.Integer", "The comparand is disposed.");
            }

            return IntegerCore.Compare(ref this.state, ref other.state);
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Disposes the object, releasing all managed and unmanaged resources associated with it/
        /// </summary>
        public void Dispose()
        {
            if (this.disposed)
            {
                return;
            }

            IntegerCore.Free(ref this.state);

            try
            {
                GC.RemoveMemoryPressure(checked(IntegerCore.StateSize + this.state.Allocated));
            }
            catch (ArithmeticException)
            {
                GC.RemoveMemoryPressure(Int32.MaxValue);
            }
            finally
            {
                GC.SuppressFinalize(this);
                this.disposed = true;
            }
        }

        #endregion

        #endregion
    }
}
